﻿using System;

namespace AdaptiveAgents.Distributions
{
    /// <summary>
    /// Represents an exponential distribution
    /// </summary>
    public class Exponential : IDistribution
    {
        //Data members
        private double _lambda = 1.0;
        private double _F0 = 0;
        private double _F1 = 0;
        private double _intervalOfNormalization = 1;
        
        /// <summary>
        /// Nomalization of the distribution
        /// </summary>
        private void normalize()
        {
            _F0 = cumulativeValueOf(0);
            _F1 = cumulativeValueOf(1);
            _intervalOfNormalization = _F1 - _F0;
        }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="lambda">λ - parameter of exponential distribution</param>
        public Exponential(double lambda)
        {
            if (lambda <= 0)
                throw new System.ArgumentException("lambda should be greater than zero", "lambda");

            _lambda = lambda;
        }

        #region IDistribution Members

        /// <summary>
        /// Gets the mean of the distribution
        /// </summary>
        public double Mean
        {
            get { return 1 / _lambda; }
        }

        /// <summary>
        /// Gets the variance of the distribution
        /// </summary>
        public double Variance
        {
            get { return 1 / (_lambda * _lambda); }
        }

        /// <summary>
        /// Generates ranodm lambda from the interval [0,10]
        /// </summary>
        public void generateRandomValues()
        {
            //Calculate lambda
            _lambda = 10 * Generator.getNextDouble();
        }


        /// <summary>
        /// Gets f(x) (probability density function)
        /// </summary>
        /// <param name="x">Random variable</param>
        /// <returns>f(x) according to the distribution function</returns>
        public double valueOf(double x)
        {
            return _lambda * Math.Pow(Math.E, -_lambda * x) / _intervalOfNormalization;
        }

        /// <summary>
        /// Gets F(x) (probability cumulative density function)
        /// </summary>
        /// <param name="x">Random variable</param>
        /// <returns>F(x) according to the exponential distribution function</returns>
        public double cumulativeValueOf(double x)
        {
            return (1 - Math.Pow(Math.E, -_lambda * x)) / _intervalOfNormalization;
        }

        /// <summary>
        /// Gets a sample of the distribution values
        /// </summary>
        /// <returns>Array of points that were sampled from the distribution function</returns>
        public Point[] sample()
        {
            int size = 100;
            double step = 0.01;
            double limit = size * step;
            int i = 0;

            Point[] sample = new Point[size];

            for (double x = 0; x < limit; x += step)
            {
                sample[i++] = new Point(x, valueOf(x));
            }

            return sample;
        }

        /// <summary>
        /// Gets the distribution type
        /// </summary>
        public DistributionType Type
        {
            get { return DistributionType.Exponential; }
        }

        /// <summary>
        /// Gets a description of the distribution
        /// </summary>
        /// <returns>String representation of the exponential distribution</returns>
        public String ToString()
        {
            return String.Format("{0}: {1}", Type.ToString(), _lambda);
        }
        
        /// <summary>
        /// Generates random value
        /// </summary>
        /// <returns>Choose random x and returns f(x)</returns>
        public double generateValue()
        {
            return valueOf(Generator.getNextDouble());
        }
        #endregion
    }
}
